;------------------------------------------------------------------------------
; Versija: 1.3
; 2009-04-28
;
; booter.asm - Operacines sistemos "Crazy Monkey" booter'is.
;
; Dokumentacija:
;   ---
;
; Booter'is EXT2 failu sistemoje susiranda boot kataloga (kuris turi buti
; pirmasis katalogas, sukurtas failu sistemoje), jame suranda loader.bin
; faila, kuris yra STAGE 2 booter'io etapas (jis turi buti pirmas failas,
; sukurtas failu sistemoje) ir uzkroves ji i atminti (0x0050:0x0000 adresu),
; atiduoda valdyma loader'iui.
;
; Pastaba:
;   boot katalogo ir loader.bin failo sukurimo tvarka yra svarbi, nes 512
;   baitu yra per mazai parasyti universaliam booter'iui.
;
;   ---
;
;   ;------------------------------------------------
;   ; (proceduros aprasas)
;   ;
;   ; Cng reg: (keiciami registrai)
;   ; In: (proceduros parametrai)
;   ; Out: (proceduros rezultatai)
;   ; Err: (klaidos salyga ir rezultatas)
;   ;------------------------------------------------
;------------------------------------------------------------------------------

;----------
; Antraste
;----------

bits 16     ; 16-bit programos kodas.
org 0       ; Programos kodo adresas atmintyje.

;----------
; Kodas
;----------

;------------------------------------------------
; Pagrindine booter'io procedura.
;------------------------------------------------
main:

  ; Nusistatome segmentus.
  cli
  mov     ax, 0x07C0
  mov     ds, ax
  mov     es, ax
  mov     fs, ax
  mov     gs, ax
  
  ; Sukuriame steka.
  mov     ax, 0x0000
  mov     ss, ax
  mov     sp, 0xFFFF
  sti

  mov [boot_drive], dl  ; Issisaugome irenginio is kurio kraunamasi koda.

  mov si, stringStage   ; I SI ikrauname eilutes pradzios adresa.
  call printString      ; Kvieciame eilutes spausdinimo procedura.

;------------------------------------------------

  reset:

    mov ah, 0x00          ; Irenginio atstatymo pertraukimas.
    mov dl, [boot_drive]  ; Irenginys, kuri atstatinesime.
    int 0x13              ; Pertraukimas.
    jc reset              ; Jeigu buvo klaida - bandome dar karta.

;------------------------------------------------

  ; Nuskaitome SuperBlock pirma sektoriu (512 baitu).
  mov byte [sector_number], 0x03  ; Treciame sektoriuje yra SuperBlock.
  call readSector

  ; Issisaugome bloko dydi.
  mov ax, 0x0018            ; 0x0018 offset'u prasideda bloko dydzio irasas.
  mov bx, block_size
  call read4Bytes

  ; Issisaugome bloku skaiciu grupeje.
  mov ax, 0x0020            ; 0x0020 offset'u prasideda irasas.
  mov bx, blocks_per_group
  call read4Bytes
  
  ; Issisaugome inode skaiciu grupeje.
  mov ax, 0x0028            ; 0x0028 offset'u prasideda irasas.
  mov bx, inodes_per_group
  call read4Bytes
  
  ; Skaiciuojame bloko dydi.
  mov ax, 0x0400                  ; Darysime 1024 (0x0400) shift left.
  mov cl, byte [block_size+0x03]
  shl ax, cl                      ; Darome shift left.
  mov word [block_size+0x02], ax
  
;------------------------------------------------

  ; Nuskaitome Group Descriptor Table bloka (512 baitu).
  mov byte [sector_number], 0x05  ; Penktame sektoriuje yra GDT.
  call readSector

  ; Issisaugome inode lenteles bloko numeri.
  mov ax, 0x0008                  ; 0x0008 offset'u prasideda irasas.
  mov bx, inode_table
  call read4Bytes

  ; Nuskaitome Inode Table bloka (512 baitu).
  mov ax, word [inode_table+0x02]
  call readBlock

  ; Issisaugome root pirmo bloko numeri.
  mov ax, 0x00a8        ; Imsime Inode 2 informacija (0x80 + 0x28 offset).
  mov bx, tmp_dd
  call read4Bytes

  ; Nuskaitome root pirma bloka (512 baitu).
  mov ax, word [tmp_dd+0x02]
  call readBlock

  ; Issisaugome boot inode bloko numeri.
  mov ax, 0x0044        ; 0x0004 offset'u prasideda bloko numeris.
  call tmpProcedure
  
  ; Issaugome boot pirmo bloko numeri.
  mov ax, word [tmp_dd+0x02]
  sub ax, 0x0001
  mov bx, 0x0080        ; Vienas inode irasas uzima 0x0080 baitu.
  mul bx
  add ax, 0x0028        ; 0x0028 offset'u prasideda adresai i atminties blokus.
  mov bx, tmp_dd
  call read4Bytes

  ; Nuskaitome boot pirma bloka (512 baitu).
  mov ax, word [tmp_dd+0x02]
  mov byte [number_of_sectors], 0x01
  call readBlock

;------------------------------------------------

  ; Issisaugome loader inode bloko numeri.
  mov ax, 0x0018        ; 0x0008 offset'u prasideda irasas.
  call tmpProcedure
  
  ; Issisaugome loader'io failo dydi.
  mov ax, word [tmp_dd+0x02]
  sub ax, 0x0001
  mov bx, 0x0080        ; Vienas inode irasas uzima 0x0080 baitu.
  mul bx
  add ax, 0x0004        ; 0x0028 offset'u prasideda failo dydzio irasas.
  mov bx, loader_size
  call read4Bytes

  ; Paskaiciuojame kiek bloku uzima loader failas.
  xor ax, ax
  mov ax, word [loader_size+0x02]
  mov bx, word [block_size+0x02]
  div bx
  add ax, 0x0001
  xor cx, cx
  mov cx, ax

  ; Ciklas nuskaitantis visus loader failo blokus.
  readLoaderBlocks:
  
    push cx   ; Issisaugome ciklo skaitliuka.
    
    ; Pereiname i atminties vieta kur yra nuskaityta informacija.
    mov ax, 0x1000
    mov es, ax
    
    ; Issivalome registrus.
    xor ax, ax
    xor bx, bx
    
    ; Issaugome loader bloko numeri.
    mov ax, word [tmp_dd+0x02]
    sub ax, 0x0001
    mov bx, 0x0080        ; Vienas inode irasas uzima 0x0080 baitu.
    mul bx
    add ax, word [offset_of]
    mov bx, loader_block
    call read4Bytes
    
    ; Pereiname i atminties vieta kur issaugosime loader'i.
    mov ax, 0x0050
    mov word [sector_to], ax
    
    ; Nuskaitome loader bloka (512 baitu).
    mov ax, word [loader_block+0x02]
    mov byte [number_of_sectors], 0x02
    call readBlock
    
    ; Offset kur rasysime kita loader bloka.
    xor ax, ax
    mov ax, word [read_to]
    add ax, word [block_size+0x02]
    mov word [read_to], ax
    
    ; Kito loader bloko offset.
    xor ax, ax
    mov ax, word [offset_of]
    add ax, 0x0004        ; Adreso ilgis yra 0x0004 baitai.
    mov word [offset_of], ax
    
    pop cx
  
  loop readLoaderBlocks

;------------------------------------------------

  ; Atiduodame valdyma loader'iui.
  push word 0x0050
  push word 0x0000
  retf
  
;------------------------------------------------
; Proceduru kvieciamu is main proceduros
; realizacijos.
;------------------------------------------------

;------------------------------------------------
; Procedura skirta sumazinti pasikartojancio
; kodo naudojima.
;
; Cng reg: BX, AX, DX
;------------------------------------------------
tmpProcedure:

  mov bx, tmp_dd
  call read4Bytes

  ;Issivalome registrus.
  xor ax, ax
  xor dx, dx
  
  ;Pasiskaiciuojame loader inode.
  mov ax, word [tmp_dd+0x02]
  call calculateInode

  ;Nuskaitome Inode Table bloka (1024 baitu) (gali buti ne 0 bloku grupe)
  mov byte [number_of_sectors], 0x04
  call readBlock
  
  ret                   ; Griztame i proceduros kvietimo vieta.

;------------------------------------------------
; Procedura perskaitanti 4 baitus is atminties
; adresu 0x1000:AX.
;
; Cng reg: AX, SI
; In: AX, BX
; Out: nuskaityti 4 baitai i BX registre
;      nurodyta adresa.
;------------------------------------------------
read4Bytes:

  mov si, ax                ; Nusistatome offset'a.
  mov ax, word [es:si]      ; Nusiskaitome zodi i AX.
  mov word [bx+0x02], ax    ; Irasome pirma zodi.
  mov ax, word [es:si+0x02]
  mov word [bx], ax

  ret                   ; Griztame i proceduros kvietimo vieta.

;------------------------------------------------
; Procedura apskaiciuojanti kokiame bloke yra
; Inode Table ir kelintas inode toje lenteleje
; yra reikalingas.
;
; Cng reg: AX, DX
; In: AX
; Out: AX - Inode leneteles bloko numeris,
;      tmp_dd - inode numeris
;------------------------------------------------
calculateInode:

  div word [inodes_per_group+0x02]  ; AX esanti inode numeri daliname is inode skaiciaus
                                    ; esanciu vineoje grupeje.
  
  push dx                           ; Issisaugome liekana.
  
  mul word [blocks_per_group+0x02]  ; Dauginame is bloku skaiciaus grupeje.
  add ax, word [inode_table+0x02]   ; Pridedame kokiame bloke yra inode lentele,
                                    ; AX registre lieka inode lenetles bloko numeris.
  
  pop dx

  mov word [tmp_dd+0x02], dx        ; Liekana yra inode numeris lenteleje.

  ret                               ; Griztame i proceduros kvietimo vieta.

;------------------------------------------------
; Procedura perskaitanti viena sektoriu is
; apskaiciuoto bloko.
;
; Cng reg: AX, DX
; In: AX
; Out: sektorius ikeltas i atminti adresu 
;      0x1000:0x0000.
;------------------------------------------------
readBlock:

  xor bx, bx
  xor dx, dx
  
  
  mov bx, 0x01f8
  div bx
  mov byte [cylinder_number], al
  
  xor ax, ax
  mov ax, dx
  
  add ax, ax            ; Padvigubiname AX reiksme, nes 1024/512=2.
  mov dl, 0x3f          ; Viena galva atitinka 0x3f bloku.
  div dl                ; Daliname is 0x3f, kad apskaiciuotume head_number.
  add ah, 0x01          ; Pridedame 0x01, kad apskaiciuotume sector_number.
  
  mov byte [sector_number], ah
  mov byte [head_number], al
  call readSector
  
  ret                   ; Griztame i proceduros kvietimo vieta.

;------------------------------------------------
; Procedura skaitanti sektorius is usb.
;
; Cng reg: AX, ES, BX, CX, DX
; In: numberOfSectors, cylinderNumber,
;     sectorNumber, headNumber, bootDrive,
;     sector_to, read_to
; Out: sektorius ikeltas i atminti adresu 
;      sector_to:read_to.
;------------------------------------------------
readSector:

  mov ax, word [sector_to]        ; Sektorius skaitysime adresu sector_to:read_to.
  mov es, ax
  xor bx, bx
  mov bx, word [read_to]

  read:

    mov ah, 0x02                      ; Sektoriu skaitymo i atminti pertraukimas.
    mov al, byte [number_of_sectors]  ; Kiek sektoriu reikia perskaityti.
    mov ch, byte [cylinder_number]    ; Cilindro numeris.
    mov cl, byte [sector_number]      ; Sektoriaus numeris.
    mov dh, byte [head_number]        ; Galvutes numeris.
    mov dl, byte [boot_drive]         ; Irenginio numeris.
    int 0x13                          ; Pertraukimas.
    jc read                           ; Jeigu buvo klaida - bandome dar karta.
 
    ret                 ; Griztame i proceduros kvietimo vieta.
 
;------------------------------------------------
; Procedura atspausdinanti eilute ekrane.
;
; Cng reg: AL, SI
; In: SI - eilutes pradzios adresas.
; Out: ekrane atspausdinta eilute.
;------------------------------------------------
printString:

  nextChar:

    mov al, [si]      ; I AL ikrauname baita i kuri rodo SI.
    inc si            ; Didiname SI vienetu, kad SI rodytu i sekanti baita.

  or al, al           ; Tikriname ar AL yra 0'is (tai reikstu eilutes pabaiga).
  jz exitPrintString  ; Jei salyga tenkinama - baigiame darba.
  call printChar      ; Priesingu atveju - kvieciame simbolio spausdinimo procedura.

  jmp nextChar        ; Imsime kita simboli spausdinimui.

  exitPrintString:
  
    ret               ; Griztame i proceduros kvietimo vieta.

;------------------------------------------------
; Procedura atspausdinanti simboli ekrane.
;
; Cng reg: AH, BH, BL
; In: AL - simbolio  ASCII kodas.
; Out: ekrane atspausdintas simbolis.
;------------------------------------------------
printChar:

  mov ah, 0x0e  ; Simbolio isvedimo i ekrana pertraukimas.
  mov bh, 0x00  ; Puslapio numeris.
  mov bl, 0x07  ; Srifto spalvos kodas (0x07 - pilka).
  int 0x10      ; Pertraukimas.

  ret           ; Griztame i proceduros kvietimo vieta.

;----------
; Duomenys
;----------

; Kintamieji readSector procedurai.
boot_drive         db 0x00
number_of_sectors  db 0x01
cylinder_number    db 0x00
sector_number      db 0x00
head_number        db 0x00

; Informacija is SuperBlock.
block_size         dd 0x00000000
blocks_per_group   dd 0x00000000
inodes_per_group   dd 0x00000000

; Informacija is Group Descriptor Table.
inode_table        dd 0x00000000  ; Kokiame sektoriuje, nuo grupes pradzios yra Inode Table.

; Papildomi kintamieji loader.bin paieskai.
loader_size   dd 0x00000000   
loader_block  dd 0x00000000   

; Adresas, kur bus skaitoma informacija is laikmenos.
read_to       dw 0x0000
sector_to     dw 0x1000

; Pirmo bloko adreso pradzia Inode aprase.
offset_of     dw 0x0028

; Papildomas laikinas kintamasis.
tmp_dd        dd 0x00000000

; Tekstine informacija.
stringStage   db 'Entering [Stage 1]...', 0x0d, 0x0a, 0x00

;----------
; Pabaiga
;----------

times 510 - ($ - $$) db 0 ; Likusius nepanaudotus baitus uzpildome 0'iais.

dw 0xaa55                 ; MBR parasas.
